package com.yourenbang.lingyun.transaction.web.service.impl.fund.fundopt;

import com.yourenbang.lingyun.constants.BizCodeMessages;
import com.yourenbang.lingyun.constants.enums.PlatNoEnums;
import com.yourenbang.lingyun.core.message.Result;
import com.yourenbang.lingyun.core.util.BizAssertUtils;
import com.yourenbang.lingyun.core.util.DingdingUtils;
import com.yourenbang.lingyun.core.util.KLog;
import com.yourenbang.lingyun.domain.FundAccount;
import com.yourenbang.lingyun.transaction.config.DingdingConfig;
import com.yourenbang.lingyun.transaction.constant.JournalStatus;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountChangePlanMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountJournalMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountMapper;
import com.yourenbang.lingyun.transaction.web.domain.dto.ChangeFundAccountV2DTO;
import com.yourenbang.lingyun.transaction.web.domain.po.FundAccountChangePlan;
import com.yourenbang.lingyun.transaction.web.domain.po.FundAccountJournal;
import com.yourenbang.lingyun.transaction.web.service.fund.fundopt.FundOptDepositService;
import com.yourenbang.transaction.api.model.dto.AddBillDTO;
import com.yourenbang.transaction.api.service.fund.FundBillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;

@Slf4j
@Service
public class FundOptDepositServiceImpl implements FundOptDepositService {

    @Autowired
    private TransactionFundAccountMapper transactionFundAccountMapper;
    @Resource
    private TransactionFundAccountJournalMapper fundAccountJournalMapper;
    @Resource
    private TransactionFundAccountChangePlanMapper fundAccountChangePlanMapper;
    @Resource
    private DingdingConfig dingdingConfig;
    @Autowired
    private FundBillService fundBillService;

    @Transactional
    @Override
    public Result<FundAccountChangePlan> deposit(FundAccount fromFundAccount, FundAccount toFundAccount,
                                                 ChangeFundAccountV2DTO changeFundAccountV2DTO) {

        if (toFundAccount.getAccountAmountWithdrawable().compareTo(changeFundAccountV2DTO.getChangeAmount()) < 0) {

            KLog.warn("【提现】可提余额冻结, 用户账户资金不足, 入参:{}", changeFundAccountV2DTO);
            return Result.builder().fail(BizCodeMessages.TRANSACTION_AMOUNT_DEFICIENCY, "用户账户资金不足");

        }

        //系统账户资金不足
        if (fromFundAccount.getAccountAmountWithdrawable().compareTo(changeFundAccountV2DTO.getChangeAmount()) < 0) {

            KLog.warn("【提现】可提余额冻结, 系统账户资金不足, 入参:{}", changeFundAccountV2DTO);
            StringBuilder dingMark = new StringBuilder("【账户余额不足预警】");
            dingMark.append(PlatNoEnums.getPlatNo(fromFundAccount.getPlatNo()).getName());
            dingMark.append(fromFundAccount.getAccountName());
            dingMark.append("余额已不足,请尽快充值!");
            DingdingUtils.sendToDingding(dingMark.toString(), dingdingConfig.getDingdingWarnToken());

            return Result.builder().fail(BizCodeMessages.TRANSACTION_AMOUNT_DEFICIENCY, "系统维护中,请稍后再试");
        }


        FundAccountChangePlan changePlan = fundAccountChangePlanMapper.getByOrderId(fromFundAccount.getPlatNo(),
                changeFundAccountV2DTO.getOrderNo());

        try {

            //资金发起方 可提现金额-;冻结中的金额+
            int i = transactionFundAccountMapper.withdrawableDeposit(fromFundAccount.getFundAccountId(),
                    changeFundAccountV2DTO.getChangeAmount());
            BizAssertUtils.assertOne(i, "更新用户账户失败");

            //记录资金发起方流水
            FundAccountJournal journal = getFromFundAccountJournal(changeFundAccountV2DTO, fromFundAccount, changePlan);
            fundAccountJournalMapper.insert(journal);

            //资金接收方 可提资金增加
            int ii = transactionFundAccountMapper.withdrawableDeposit(toFundAccount.getFundAccountId(),
                    changeFundAccountV2DTO.getChangeAmount());
            BizAssertUtils.assertOne(ii, "更新用户账户失败");

            //记录资金接收起方流水
            FundAccountJournal tojournal = getToFundAccountJournal(changeFundAccountV2DTO, toFundAccount, changePlan);
            fundAccountJournalMapper.insert(tojournal);

            addBill(changeFundAccountV2DTO, toFundAccount, changePlan);

        } catch (Exception e) {

            KLog.error("【提现】可提余额冻结,异常:{}, 资金变动记录:{}", e.getMessage(), changeFundAccountV2DTO.toString(), e);
            return Result.builder().fail(BizCodeMessages.TRANSACTION_PLEDGE_CHECKING);

        }

        KLog.info("【提现】可提余额冻结, 出参:{}, 入参:{}", changeFundAccountV2DTO, changePlan);
        return Result.builder().success(changePlan);
    }


    @Transactional
    @Override
    public Result<FundAccountChangePlan> depositBalance(FundAccount fromFundAccount, FundAccount toFundAccount,
                                                        ChangeFundAccountV2DTO changeFundAccountV2DTO) {

        if (fromFundAccount.getAccountAmountWithdrawable().compareTo(changeFundAccountV2DTO.getChangeAmount()) < 0) {

            KLog.warn("【提现】可提余额冻结, 用户账户资金不足, 入参:{}", changeFundAccountV2DTO);
            return Result.builder().fail(BizCodeMessages.TRANSACTION_AMOUNT_DEFICIENCY, "用户账户资金不足");

        }


        FundAccountChangePlan changePlan = fundAccountChangePlanMapper.getByOrderId(fromFundAccount.getPlatNo(),
                changeFundAccountV2DTO.getOrderNo());

        try {

            //资金发起方 可提现金额-;冻结中的金额+
            int i = transactionFundAccountMapper.withdrawableDeposit(fromFundAccount.getFundAccountId(),
                    changeFundAccountV2DTO.getChangeAmount());
            BizAssertUtils.assertOne(i, "更新用户账户失败");

            //记录资金发起方流水
            FundAccountJournal journal = getFromFundAccountJournal(changeFundAccountV2DTO, fromFundAccount, changePlan);
            fundAccountJournalMapper.insert(journal);

            addBill(changeFundAccountV2DTO, fromFundAccount, changePlan);

        } catch (Exception e) {

            KLog.error("【提现】可提余额冻结,异常:{}, 资金变动记录:{}", e.getMessage(), changeFundAccountV2DTO.toString(), e);
            return Result.builder().fail(BizCodeMessages.TRANSACTION_CHANGE_PLAN_ERROR);

        }

        KLog.info("【提现】可提余额冻结, 出参:{}, 入参:{}", changeFundAccountV2DTO, changePlan);
        return Result.builder().success(changePlan);
    }

    private FundAccountJournal getFromFundAccountJournal(ChangeFundAccountV2DTO changeFundAccountDTO, FundAccount fromFundAccount,
                                                         FundAccountChangePlan changePlan) {

        FundAccountJournal journal = new FundAccountJournal(changePlan.getPlatNo());
        journal.setChannelId(changePlan.getChannelId());
        journal.setFundAccountChangePlanId(changePlan.getFundAccountChangePlanId());
        journal.setFundAccountId(fromFundAccount.getFundAccountId());
        journal.setJournalStatus(JournalStatus.SUCCESS);
        journal.setJournalChangeValue(changePlan.getChangeAmount());
        journal.setAccountJournalAcid(changePlan.getOrderId());

        journal.setAccountMainJournalType(changeFundAccountDTO.getJournalInfo().getAccountMainJournalType().getCode());
        journal.setAccountMainJournalTypeName(changeFundAccountDTO.getJournalInfo().getAccountMainJournalType().getName());

        journal.setAccountIncome(BigDecimal.ZERO);            //收入资金变动金额
        journal.setAccountAmountFrozen(changePlan.getChangeAmount());   //冻结资金变动收入
        journal.setAccountAmountWithdrawable(changePlan.getChangeAmount());    //可提现资金变动收入
        journal.setAccountAmountWithdrawed(BigDecimal.ZERO);  //已提现资金变动收入
        journal.setAccountAmountUndepositable(BigDecimal.ZERO);   //不可提现资金变动收入

        journal.setAccountIncomeBefore(fromFundAccount.getAccountIncome());      //收入变动前的金额
        journal.setAccountIncomeAfterwards(fromFundAccount.getAccountIncome());  //收入变动后的金额

        journal.setAccountAmountFrozenBefore(fromFundAccount.getAccountAmountFrozen());   //冻结资金变化前金额
        journal.setAccountAmountFrozenAfterwards(fromFundAccount.getAccountAmountFrozen().add(changePlan.getChangeAmount())); //冻结资金变化后金额

        journal.setAccountAmountWithdrawableBefore(fromFundAccount.getAccountAmountWithdrawable());   //可提现金额变化前金额
        journal.setAccountAmountWithdrawableAfterwards(fromFundAccount.getAccountAmountWithdrawable().subtract(changePlan.getChangeAmount())); //可提现金额变化后金额

        journal.setAccountAmountWithdrawedBefore(fromFundAccount.getAccountAmountWithdrawed());        //已提现金额变化前金额
        journal.setAccountAmountWithdrawedAfterwards(fromFundAccount.getAccountAmountWithdrawed());    //已提现金额变化后金额

        journal.setAccountAmountUndepositableBefore(fromFundAccount.getAccountAmountUndepositable());     //不可提现金额变化前金额
        journal.setAccountAmountUndepositableAfterwards(fromFundAccount.getAccountAmountUndepositable()); //不可提现金额变化后金额


        return journal;
    }

    private FundAccountJournal getToFundAccountJournal(ChangeFundAccountV2DTO changeFundAccountDTO, FundAccount toFundAccount,
                                                       FundAccountChangePlan changePlan) {

        FundAccountJournal journalUser = new FundAccountJournal(changePlan.getPlatNo());
        journalUser.setChannelId(changePlan.getChannelId());
        journalUser.setFundAccountChangePlanId(changePlan.getFundAccountChangePlanId());
        journalUser.setFundAccountId(toFundAccount.getFundAccountId());
        journalUser.setJournalStatus(JournalStatus.SUCCESS);
        journalUser.setJournalChangeValue(changePlan.getChangeAmount());
        journalUser.setAccountJournalAcid(changePlan.getOrderId());

        journalUser.setAccountMainJournalType(changeFundAccountDTO.getJournalInfo().getAccountMainJournalType().getCode());
        journalUser.setAccountMainJournalTypeName(changeFundAccountDTO.getJournalInfo().getAccountMainJournalType().getName());

        journalUser.setAccountIncome(BigDecimal.ZERO);            //收入资金变动金额
        journalUser.setAccountAmountFrozen(changePlan.getChangeAmount());   //冻结资金变动收入
        journalUser.setAccountAmountWithdrawable(changePlan.getChangeAmount());    //可提现资金变动收入
        journalUser.setAccountAmountWithdrawed(BigDecimal.ZERO);  //已提现资金变动收入
        journalUser.setAccountAmountUndepositable(BigDecimal.ZERO);   //不可提现资金变动收入

        journalUser.setAccountIncomeBefore(toFundAccount.getAccountIncome());      //收入变动前的金额
        journalUser.setAccountIncomeAfterwards(toFundAccount.getAccountIncome());  //收入变动后的金额

        journalUser.setAccountAmountFrozenBefore(toFundAccount.getAccountAmountFrozen());   //冻结资金变化前金额
        journalUser.setAccountAmountFrozenAfterwards(toFundAccount.getAccountAmountFrozen().add(changePlan.getChangeAmount())); //冻结资金变化后金额

        journalUser.setAccountAmountWithdrawableBefore(toFundAccount.getAccountAmountWithdrawable());   //可提现金额变化前金额
        journalUser.setAccountAmountWithdrawableAfterwards(toFundAccount.getAccountAmountWithdrawable().subtract(changePlan.getChangeAmount())); //可提现金额变化后金额

        journalUser.setAccountAmountWithdrawedBefore(toFundAccount.getAccountAmountWithdrawed());        //已提现金额变化前金额
        journalUser.setAccountAmountWithdrawedAfterwards(toFundAccount.getAccountAmountWithdrawed());    //已提现金额变化后金额

        journalUser.setAccountAmountUndepositableBefore(toFundAccount.getAccountAmountUndepositable());     //不可提现金额变化前金额
        journalUser.setAccountAmountUndepositableAfterwards(toFundAccount.getAccountAmountUndepositable()); //不可提现金额变化后金额

        return journalUser;

    }

    private void addBill(ChangeFundAccountV2DTO changeFundAccountDTO, FundAccount toFundAccount,
                         FundAccountChangePlan changePlan) {
        //账单
        AddBillDTO billDTO = AddBillDTO.builder()
                .userBaseId(toFundAccount.getUserBaseId())
                .fundAccountId(toFundAccount.getFundAccountId())
                .fundAccountChangePlanId(changePlan.getFundAccountChangePlanId())
                .orderId(changeFundAccountDTO.getOrderNo())
                .amount(changeFundAccountDTO.getChangeAmount().negate())
                .billType(changeFundAccountDTO.getFundBillType())
                .channelId(changeFundAccountDTO.getChannelId())
                .platNo(changePlan.getPlatNo())
                .build();

        billDTO.setAccountMainJournalType(changeFundAccountDTO.getJournalInfo().getAccountMainJournalType().getCode());
        billDTO.setAccountMainJournalTypeName(changeFundAccountDTO.getJournalInfo().getAccountMainJournalType().getName());

        fundBillService.addBill(billDTO);
    }
}
